package fregat.connector.worker;

import fregat.base.util.Logger;
import fregat.connector.ConnectorConfiguration;
import fregat.connector.ConnectorService;
import fregat.connector.base.DataFilter;
import fregat.connector.channel.ChannelListener;

import java.io.PrintStream;
import java.util.StringTokenizer;

/**
 * Default implementation for a worker that just logs the received data.
 *
 * @author Alexander Mann <br><b><i>ECO_L O G</i> &nbsp Logistiksysteme GmbH</b>
 * @version 1.0
 */
public class DefaultWorker implements ChannelListener {
  protected String name; // configuration name of worker
  protected String channelName; // configuration name of channel where data is sent
  protected ConnectorConfiguration configuration; // configuration wrapper
  protected boolean shutdown; // shutdown flag
  protected boolean connected; // connection state, true if connected

  private DataFilter filter; // filters received data
  private boolean standalone; // flag, whether worker has no channel

  /**
   * Creates a default worker.
   *
   * @param name Configuration name
   */
  public DefaultWorker(String name) {
    this.name = name;
    configuration = ConnectorService.getConfiguration();

    if (configuration != null) {
      String configPath = "worker.config." + name;

      if (configuration.getConfigurationBoolean(configPath + ".standalone")) {
        // stand alone worker
        standalone = true;
        System.out.println(ConnectorService.resource.getString("DefaultWorker_constructor_5"));

      } else {
        // configure channel -->
        channelName = configuration.getConfigurationString(configPath + ".channel");
        if (channelName == null) {
          ConnectorService.warn(ConnectorService.resource.getString("DefaultWorker_constructor_1").
            replaceFirst("%1", getName()) + "," +
            ConnectorService.resource.getString("DefaultWorker_constructor_2").
            replaceFirst("%1", getName()));
          channelName = getName();
        } else {
          System.out.println(ConnectorService.resource.getString("DefaultWorker_constructor_2").
            replaceFirst("%1", getName()));
        }
        // <--

        // configure filter(s) -->
        String filters = configuration.getConfigurationString(configPath + ".filter");
        if (filters != null) {
          filter = new DataFilter();
          if (filters.equalsIgnoreCase("ALL")) {
            // block all messages
            filter.filterAll();

          } else {
            // only block specific messages
            StringTokenizer filterTokenizer = new StringTokenizer(filters, " ");

            while (filterTokenizer.hasMoreTokens()) {
              String rule = filterTokenizer.nextToken();
              StringTokenizer tokenizer = new StringTokenizer(rule, ",");

              try {
                int position = Integer.parseInt(tokenizer.nextToken());
                String value = tokenizer.nextToken();
                filter.addRule(position, value);
                System.out.print(ConnectorService.resource.getString("DefaultWorker_constructor_3"));

              } catch (Exception ex) {
                System.out.print(ConnectorService.resource.getString("DefaultWorker_constructor_4"));
              }
              System.out.println(" '" + rule + "'");
            }
          }
        }
        // <--
      }
    }
  }

  /**
   * @return Configuration name of worker
   */
  public String getName() {
    return name;
  }

  /**
   * Default implementation that logs the received data.
   *
   * @param channelName  Configuration name of channel where data was received
   * @param receivedData Received data
   */
  protected boolean dataReceived(String channelName, String receivedData) {
    String str = getName() + ", " + ConnectorService.resource.getString("DefaultWorker_dataReceived_1") +" "+channelName+ " '" + receivedData + "'";
    ConnectorService.debug(str);
    Logger.getCategory(ConnectorService.COMMUNICATION_LOGGER).info(str);
    return true;
  }

  /**
   * Called by channel when data was received.
   *
   * @param channelName  Configuration name of channel where data was received
   * @param receivedData Received data
   */
  public final boolean received(String channelName, byte[] receivedData) {
    String data = new String(receivedData);
    if (filter == null || (filter != null && filter.process(data, getName()))) {
      return dataReceived(channelName, data);
    }
    return true;
  }

  /**
   * Send data using the specified channel
   *
   * @param channelName Configuration name of channel
   * @param sendData    Data to be sent
   * @return True, if send was successful
   */
  public final boolean send(String channelName, String sendData) {
    if (sendData != null) {
      return ConnectorService.send(channelName, sendData.getBytes());
    } else {
      ConnectorService.warn(getName() + ", send data is null");
      return false;
    }
  }

  /**
   * Starts the worker(default implementation).
   */
  public void start() {
    shutdown = false;
  }

  /**
   * Stops the worker(default implementation).
   */
  public void shutdown() {
    shutdown = true;
  }

  /**
   * Informs the listener about the connection state.
   *
   * @param b True, if connected
   */
  public void connected(boolean b) {
    connected = b;
  }

  /**
   * Set worker as stand alone (connected to no channel).
   *
   * @param b True, if standalone
   */
  public void setStandalone(boolean b) {
    standalone = b;
  }

  /**
   * @return True, if standalone (connected to no channel)
   */
  public boolean isStandalone() {
    return standalone;
  }

  /**
   * Executes the base console commands to start or stop a 'standalone' worker.
   *
   * @param command Command string from console
   * @param out     Stream where output should be printed on
   */
  public void receivedCommand(String command, PrintStream out) {
    if (command.equals("?")) {
      // list commands
      if (standalone) {
        out.println(ConnectorService.resource.getString("DefaultWorker_receivedCommand_4"));
        out.println(ConnectorService.resource.getString("DefaultWorker_receivedCommand_5"));
      }
      out.println(ConnectorService.resource.getString("DefaultWorker_receivedCommand_6"));

    } else if (command.equalsIgnoreCase("START") ||
      command.equalsIgnoreCase("STOP")) {
      // start or stop 'standalone' worker
      String operation;
      if (command.equalsIgnoreCase("START")) {
        operation = ConnectorService.resource.getString("DefaultWorker_receivedCommand_2");
      } else {
        operation = ConnectorService.resource.getString("DefaultWorker_receivedCommand_3");
      }

      if (standalone) {
        out.println("'" + getName() + "' " + operation);
        if (command.equalsIgnoreCase("START")) {
          // start worker
          start();
        } else if (command.equalsIgnoreCase("STOP")) {
          // stop worker
          shutdown();
        }
      } else {
        out.println("'" + getName() + "' " +
          ConnectorService.resource.getString("DefaultWorker_receivedCommand_1")
          .replaceFirst("%1", operation) + "\r");
      }
    }
  };

  public String toString() {
    return "'" + getName() + "' (" + getClass().getName() + ")";
  }
}
